﻿
Imports System.Net.Sockets
Imports tcpTrans

Namespace tcpTrans3
    Public Module tcpTrans3
        Private ReadOnly TCPTRANS3_SEND_TICKS As Int64
        Private ReadOnly TCPTRANS3_SEND_BYTES_MS As Int64
        Private ReadOnly TCPTRANS3_RECEIVE_DATASIZEPREFIX_TICKS As Int64
        Private ReadOnly TCPTRANS3_RECEIVE_TICKS As Int64
        Private ReadOnly TCPTRANS3_RECEIVE_BYTES_MS As Int64

        Public Function send(ByVal tcpClient As TcpClient,
                             ByVal data As nettransData,
                             ByVal holdTime As Int64,
                             Optional ByRef action As callbackAction = Nothing,
                             Optional ByVal legacyDataSize As Boolean = False) As Boolean
            Dim buff() As Byte = Nothing
            Dim size As Int64
            Dim init As ternary = Nothing
            Dim stream As NetworkStream = Nothing
            init = initial_send(tcpClient, stream, holdTime, data, buff, size, legacyDataSize)
            If init.notunknown() Then
                Return init.true_()
            End If

            Dim startTime As Int64 = ticksToMilliseconds(Now.ticks)
            Dim rtn As Boolean
            Dim writeAction As callbackAction = Nothing
            If writestream3(stream, buff, length:=size,
                            starttime:=startTime, holdtime:=holdTime, action:=writeAction) Then
                action = New callbackAction(check:=callbackAction.action_check(writeAction),
                                            end:=Function() As Boolean
                                                     [erase](buff)
                                                     If callbackAction.success_finished(writeAction) Then
                                                         counter.increase(TCPTRANS3_SEND_BYTES_MS,
                                                                          (size + dataSizePrefixLength) \
                                                                          safe_div_duration(startTime))
                                                         counter.recordTimeTicks(TCPTRANS3_SEND_TICKS,
                                                                                 millisecondsToTicks(startTime))
                                                         Return True
                                                     Else
                                                         raiseError(errorHandle.displayMode.console,
                                                                    "failed to send data to " +
                                                                    remoteEndPoint(tcpClient),
                                                                    errorHandle.errorType.exclamation)
                                                         Return False
                                                     End If
                                                 End Function,
                                            timeoutticks:=millisecondsToTicks(holdTime))
                rtn = begin(action)
            Else
                raiseError(errorHandle.displayMode.console, "failed to start sending data to " + _
                           remoteEndPoint(tcpClient), errorHandle.errorType.exclamation)
                [erase](buff)
                rtn = False
            End If

            Return rtn
        End Function

        Public Function receive(ByVal tcpClient As TcpClient,
                                ByRef data As nettransData,
                                ByVal holdTime As Int64,
                                Optional ByVal needOvertimeOrNotConnectedInfo As Boolean = True,
                                Optional ByVal needClearData As Boolean = True,
                                Optional ByRef action As callbackAction = Nothing,
                                Optional ByVal legacyDataSize As Boolean = False) As Boolean
            Dim init As ternary = Nothing
            Dim stream As NetworkStream = Nothing
            init = initial_receive(tcpClient, stream, holdTime, data, needOvertimeOrNotConnectedInfo, needClearData)
            If init.notunknown() Then
                Return init.true_()
            End If

            Dim d As nettransData = Nothing
            d = data
            Dim act As callbackAction = Nothing
            Dim rst As pointer(Of bytes) = Nothing
            rst = New pointer(Of bytes)()
            Dim dataLength As Int64
            Dim rtn As Boolean = False
            Dim starttime As Int64 = 0

            Const s_waiting As UInt32 = stepCallbackAction.initial_step
            Const s_prefix As UInt32 = s_waiting + 1
            Const s_data As UInt32 = s_prefix + 1
            action = New stepCallbackAction(
                            Function(ByRef s As UInt32) As Boolean
                                Select Case s
                                    Case s_waiting
                                        Return True
                                    Case s_prefix
                                        starttime = Now.milliseconds()
                                        Return readstream3(stream,
                                                           length:=dataSizePrefixLength,
                                                           starttime:=starttime,
                                                           holdtime:=holdTime,
                                                           action:=act,
                                                           result:=rst)
                                    Case s_data
                                        Return readstream3(stream,
                                                           length:=dataLength,
                                                           action:=act,
                                                           result:=rst)
                                    Case Else
                                        Return assert(False)
                                End Select
                            End Function,
                            Function(ByRef s As UInt32) As Boolean
                                Select Case s
                                    Case s_waiting
                                        If readyToReceive(tcpClient) Then
                                            s = s_prefix
                                            Return callbackAction.check_pass
                                        Else
                                            Return callbackAction.check_stay
                                        End If
                                    Case Else
                                        assert(Not act Is Nothing)
                                        Select Case s
                                            Case s_prefix
                                                Dim failureAction As void(Of UInt32) =
                                                    Sub(ByRef _step As UInt32)
                                                        raiseError(errorHandle.displayMode.console,
                                                                   "cannot get head information, skip receiving from " +
                                                                    remoteEndPoint(tcpClient),
                                                                    errorHandle.errorType.exclamation)
                                                        _step = stepCallbackAction.finished_step
                                                    End Sub
                                                Return stepCallbackAction.action_check(
                                                            act,
                                                            Sub(ByRef _step As UInt32)
                                                                If tcpTrans.dataLength(+rst, dataLength, legacyDataSize) Then
                                                                    counter.recordTimeTicks(
                                                                                TCPTRANS3_RECEIVE_DATASIZEPREFIX_TICKS,
                                                                                millisecondsToTicks(starttime))
                                                                    _step = s_data
                                                                Else
                                                                    failureAction(_step)
                                                                End If
                                                            End Sub,
                                                            failureAction,
                                                            s)
                                            Case s_data
                                                Return stepCallbackAction.action_check(
                                                            act,
                                                            Sub(ByRef _step As UInt32)
                                                                counter.increase(TCPTRANS3_RECEIVE_BYTES_MS,
                                                                                 (dataLength + dataSizePrefixLength) \
                                                                                 safe_div_duration(starttime))
                                                                counter.recordTimeTicks(TCPTRANS3_RECEIVE_TICKS,
                                                                                        millisecondsToTicks(starttime))
                                                                assert((+rst).size() = dataLength)
                                                                d.import(+rst, 0, dataLength)
                                                                rtn = True
                                                                _step = stepCallbackAction.finished_step
                                                            End Sub,
                                                            Sub(ByRef _step As UInt32)
                                                                raiseError(errorHandle.displayMode.console,
                                                                           "failed to receive data from " +
                                                                           remoteEndPoint(tcpClient),
                                                                           errorHandle.errorType.exclamation)
                                                                _step = stepCallbackAction.finished_step
                                                            End Sub,
                                                            s)
                                            Case Else
                                                Return assert(False)
                                        End Select
                                End Select
                            End Function,
                            Function() As Boolean
                                Return rtn
                            End Function,
                            timeoutticks:=millisecondsToTicks(holdTime))

            Return begin(action)
        End Function

        Sub New()
            TCPTRANS3_RECEIVE_DATASIZEPREFIX_TICKS =
                counter.registerAverageAndLastAverage("TCPTRANS3_RECEIVE_DATASIZEPREFIX_TICKS")
            TCPTRANS3_RECEIVE_TICKS = counter.registerAverageAndLastAverage("TCPTRANS3_RECEIVE_TICKS")
            TCPTRANS3_RECEIVE_BYTES_MS = counter.registerAverageAndLastAverage("TCPTRANS3_RECEIVE_BYTES_MS")
            TCPTRANS3_SEND_TICKS = counter.registerAverageAndLastAverage("TCPTRANS3_SEND_TICKS")
            TCPTRANS3_SEND_BYTES_MS = counter.registerAverageAndLastAverage("TCPTRANS3_SEND_BYTES_MS")
        End Sub
    End Module
End Namespace
